• Steven Ponce
  • About
  • Data Visualizations
  • Projects
  • Resume
  • {{< iconify fa6-brands bluesky size=1.1em title='Bluesky' >}}
  • Email

On this page

  • Static
  • Steps to Create this Graphic
    • 1. Load Packages & Setup
    • 2. Read in the Data
    • 3. Examine the Data
    • 4. Tidy Data
    • 5. Visualization Parameters
    • 6. Plot (Interactive)
    • 7. Save
    • 8. Session Info
    • 9. GitHub Repository

Orca Encounter Observations: Leading Vessels

  • Show All Code
  • Hide All Code

  • View Source

Highlighting vessels with more than one recorded orca encounter in the Salish Sea region

TidyTuesday
Data Visualization
R Programming
2024
Author

Steven Ponce

Published

October 14, 2024

Static

Figure 1: A bar chart displaying the number of orca encounters in the Salish Sea region, along with a map showing the locations of the encounters. Orcinus and Mike 1 are the vessels with the highest number of encounters, each exceeding 200.

NOTE: We used runtime: shiny to make the interactive features of our visualizations work smoothly, especially the interactive orca map. This let us add dynamic, user-driven exploration that wasn’t possible with the usual Quarto options (I needed help figuring it out!).

While it means the final result looks more like markdown and less like a sleek Quarto document, it was the best way to ensure the interactivity worked well.

Steps to Create this Graphic

1. Load Packages & Setup

Show code
```{r}
#| label: load
#| warning: false

## 1. LOAD PACKAGES & SETUP ----
pacman::p_load(
    tidyverse,         # Easily Install and Load the 'Tidyverse'
    ggtext,            # Improved Text Rendering Support for 'ggplot2'
    showtext,          # Using Fonts More Easily in R Graphs
    janitor,           # Simple Tools for Examining and Cleaning Dirty Data
    skimr,             # Compact and Flexible Summaries of Data
    scales,            # Scale Functions for Visualization
    lubridate,         # Make Dealing with Dates a Little Easier
    glue,              # Interpreted String Literals
    patchwork,         # The Composer of Plots
    here,              # A Simpler Way to Find Your Files
    sf,                # Simple Features for R
    ggiraph,           # Make 'ggplot2' Graphics Interactive
    htmltools,         # Tools for HTML
    rnaturalearth,     # World Map Data from Natural Earth 
    rnaturalearthhires # High Resolution World Vector Map Data from Natural Earth used inrnaturalearth
)  

# Note: disabled { camcorder }. Issues with plot rendering (ggiraph)

# ### |- figure size ---- 
# camcorder::gg_record(
#     dir    = here::here("temp_plots"),
#     device = "png",
#     width  =  10,
#     height =  10,
#     units  = "in",
#     dpi    = 320
# )

### |- resolution ----
showtext_opts(dpi = 320, regular.wt = 300, bold.wt = 800)
```

2. Read in the Data

Show code
```{r}
#| label: read
#| include: true
#| eval: true
#| warning: false

tt <-tidytuesdayR::tt_load(2024, week = 42) 

orcas_data <- tt$orcas |> clean_names() 

tidytuesdayR::readme(tt)
rm(tt)
```

3. Examine the Data

Show code
```{r}
#| label: examine
#| include: true
#| eval: true
#| results: 'hide'
#| warning: false

glimpse(orcas_data)
```

4. Tidy Data

Show code
```{r}
#| label: tidy
#| warning: false

### |- tidy data ----
orcas_data_clean <- orcas_data |>
    filter(!is.na(vessel)) |>
    # Standardize 'ids_encountered' to be comma-separated
    mutate(
        ids_encountered = str_replace_all(ids_encountered, "and", ","),
        ids_encountered = str_replace_all(ids_encountered, ",[[:space:]]*", ", "),
        ids_encountered = str_trim(ids_encountered)
    ) |>  
    # Extract numeric duration from the 'duration' column and convert to minutes
    mutate(
        duration_minutes = str_extract(duration, "[[:digit:]]+") |> as.numeric() / 60,
        duration_minutes = ifelse(duration_minutes < 0, NA, duration_minutes)
    ) |>
    # Convert 'date' to proper Date class and create month column
    mutate(
        date = as.Date(date),
        month = month(date, label = TRUE, abbr = FALSE)
    ) |>
    # Handle missing values
    filter(!is.na(encounter_number), !is.na(begin_latitude), !is.na(begin_longitude))


# ### |- data for bar plot ----

# Number of Encounters per Vessel
bar_plot_data <- orcas_data_clean |>
    filter(!is.na(vessel)) |>
    count(vessel, sort = TRUE) |>
    filter(n > 1) |>
    mutate(
        vessel = str_wrap(vessel, width = 20),
        vessel = fct_reorder(vessel, -n)
    )
```

5. Visualization Parameters

Show code
```{r}
#| label: params
#| include: true
#| warning: false

### |- plot aesthetics ----
bkg_col      <- colorspace::lighten('#f7f5e9', 0.05)    
title_col    <- "gray20"           
subtitle_col <- "gray20"     
caption_col  <- "gray30"   
text_col     <- "gray20"    
col_palette  <- paletteer::paletteer_d("lisa::OdilonRedon")[c(1,2)] 
# show_col(col_palette)

### |-  titles and caption ----
# icons
tt <- str_glue("#TidyTuesday: { 2024 } Week { 42 } &bull; Source: Center for Whale Research<br>")
li <- str_glue("<span style='font-family:fa6-brands'>&#xf08c;</span>")
gh <- str_glue("<span style='font-family:fa6-brands'>&#xf09b;</span>")
mn <- str_glue("<span style='font-family:fa6-brands'>&#xf4f6;</span>")

# text
title_text    <- str_glue("Orca Encounter Observations: Leading Vessels")
subtitle_text <- str_glue("Highlighting vessels with more than one recorded orca encounter in the Salish Sea region.")
caption_text  <- str_glue("{tt} {li} stevenponce &bull; {mn} @sponce1(graphic.social) {gh} poncest &bull; #rstats #ggplot2")

### |-  fonts ----
# font_add("fa6-brands", "personal-website/fonts/6.4.2/Font Awesome 6 Brands-Regular-400.otf")
font_add_google("Oswald", regular.wt = 400, family = "title")
font_add_google("Merriweather Sans", regular.wt = 400, family = "subtitle")
font_add_google("Merriweather Sans", regular.wt = 400, family = "text")
font_add_google("Noto Sans", regular.wt = 400, family = "caption")
showtext_auto(enable = TRUE)

### |-  plot theme ----
theme_set(theme_minimal(base_size = 14, base_family = "text"))                

theme_update(
    plot.title.position   = "plot",
    plot.caption.position = "plot",
    legend.position       = 'plot',
    
    plot.background       = element_rect(fill = bkg_col, color = bkg_col),
    panel.background      = element_rect(fill = bkg_col, color = bkg_col),
    plot.margin           = margin(t = 10, r = 10, b = 5, l = 10),
    
    panel.grid.minor      = element_blank(),
    panel.grid.major.y    = element_blank(),
    panel.grid.major.x    = element_line(linetype = "dotted", linewidth = 0.2, color = 'gray'),
    
    axis.title.x          = element_text(margin = margin(10, 0, 0, 0), size = rel(0.85), 
                                         color = text_col, family = "text", face = "bold", hjust = 0.5),
    axis.title.y          = element_text(margin = margin(0, 10, 0, 0), size = rel(0.85), 
                                         color = text_col, family = "text", face = "bold", hjust = 0.5),
    
    axis.text.y           = element_text(color = text_col, family = "text", size = rel(0.65)),
    axis.text.x           = element_text(color = text_col, family = "text", size = rel(0.65)),
    
    axis.ticks.x          = element_line(color = text_col),  
    axis.line.x           = element_line(color = "#252525", linewidth = .2)
)
```

6. Plot (Interactive)

Show code
```{r}
#| label: plot
#| warning: false

### |- world map for background ----
world <- st_as_sf(rnaturalearth::ne_countries(scale = 'large', returnclass = 'sf'))

### |- Base map ----

# Focus on the Salish Sea region
map <- ggplot() +
    
    # Geoms
    geom_sf(data = world, fill = "lightgrey", color = "white") +
    geom_sf_interactive(
        data = st_as_sf(orcas_data_clean, coords = c("begin_longitude", "begin_latitude"), crs = 4326),
        aes(tooltip = paste("Date:", date, "<br>Location:", location), data_id = vessel), 
        color = col_palette[1],
        alpha = 0.5,
        shape = 1,
        size = 1
    ) +
    
    # Scales
    coord_sf(xlim = c(-127, -121), ylim = c(47, 51), expand = FALSE) +          # Focus on the Salish Sea region
    
    # Labs 
    labs(
        title = "",
        x = "Longitude",
        y = "Latitude"
    ) +
    
    # Theme
    ggthemes::theme_map() 


### |- Bar plot plot ----

# Number of Encounters per Vessel
bar <- ggplot(bar_plot_data, aes(x = vessel, -n, y = n)) +
    
    # Geom
    geom_bar_interactive(
        aes(
            tooltip = paste("Vessel:", vessel, "<br>Encounters:", n), 
            data_id = vessel), 
        stat = "identity", 
        fill = col_palette[1]
    ) +
    
    # Scale
    scale_x_discrete() +
    scale_y_continuous() +    
    coord_flip() +
    
    # Labs
    labs(
        x = "Vessel",
        y = "Number of Encounters",
        title = title_text,
        subtitle = subtitle_text,
        caption = caption_text
    )  +
    
    # Theme
    theme(
        plot.title = element_text(
            size = rel(1.3),
            family = "title",
            face = "bold",
            color = title_col,
            lineheight = 1.1,
            margin = margin(t = 5, b = 5)
        ),
        plot.subtitle = element_text(
            size = rel(0.65),
            family = "subtitle",
            color = subtitle_col,
            lineheight = 1.1,
            margin = margin(t = 5, b = 5)
        ),
        plot.caption = element_markdown(    
            size = rel(0.40),
            family = "caption", 
            color = caption_col,
            lineheight = 1.1,
            hjust = 0.5,
            halign = 1,
            margin = margin(t = 5, b = 5)
        )
    )

### |- inset element ----

# Insert the map into the upper right corner of the bar plot 
combined_plot <- bar + 
    inset_element(map, left = 0, bottom = 0.2, right = 1.3, top = 1.05)
```
Show code
```{r}
#| label: interactive
#| eval: true
#| include: true
#| fig.width: 8
#| fig.height: 6
#| warning: false

### |-  interactive plots ----

# Create the interactive plot with ggiraph
interactive_plot <- girafe(
  ggobj = combined_plot,
  bg = bkg_col,
  options = list(
    opts_tooltip(
      opacity = 0.8, use_fill = TRUE,
      use_stroke = FALSE,
      css = "padding:5pt;font-family: 'Open Sans', sans-serif;font-size:1.2rem;color:white"),
    opts_selection(type = "single", css = "fill:yellow;stroke:black;stroke-width:2px;"),
    opts_sizing(rescale = TRUE),
    opts_toolbar(saveaspng = TRUE),
    opts_hover_inv(css = "opacity:0.4"),
    opts_hover(
      css = girafe_css(
        css = glue("fill:{col_palette[2]};"),
        text = "stroke:none;fill:white;fill-opacity:1;")
    )
  )
)

# Use tagList to render it properly in Quarto
htmltools::tagList(interactive_plot)
```
Back to top